home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / Digsby build 37 / digsby_setup.exe / lib / msn / MSNBuddy.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-13  |  41KB  |  801 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. import common
  5. import contacts
  6. from util import Storage, to_storage, dyn_dispatch, tag, odict, threaded, urlcacheopen, cproperty, get
  7. from util.observe import ObservableProperty
  8. from common.sms import validate_sms, normalize_sms
  9. from util.callbacks import callsback
  10. from MSNUtil import url_decode
  11. import time
  12. import datetime
  13. import logging
  14. log = logging.getLogger('msn.buddy')
  15.  
  16. yesterday = lambda : datetime.datetime.today() - datetime.timedelta(1)
  17. import sys
  18. thismod = sys.modules[__name__]
  19. del sys
  20.  
  21. try:
  22.     _
  23. except:
  24.     
  25.     _ = lambda s: s
  26.  
  27. statuses = Storage(brb = _('Be Right Back'), phone = _('On the Phone'), lunch = _('Out to Lunch'))
  28.  
  29. class MSNBuddy(common.buddy):
  30.     __slots__ = '\n    status_message\n    msn_obj\n    _idle_start\n    pending_auth\n    phone_home\n    phone_work\n    phone_mobile\n    allow_mobile\n    enable_wireless\n    remote_alias\n    has_blog\n    client_id\n    role_ids\n    get_profile\n    _space\n    membersoap\n    contactsoap\n    '.split()
  31.     
  32.     def __init__(self, msn, name = None):
  33.         self._status = 'unknown'
  34.         self._status_message = ''
  35.         self._got_presence = False
  36.         self.phone_home = None
  37.         self.phone_work = None
  38.         self.phone_mobile = None
  39.         self.allow_mobile = None
  40.         self.enable_wireless = None
  41.         self.remote_alias = None
  42.         self.has_blog = None
  43.         self.msn_obj = None
  44.         self._idle_start = 0
  45.         self.info = { }
  46.         self.role_ids = { }
  47.         self.pending_auth = False
  48.         common.buddy.__init__(self, url_decode(name), msn)
  49.         if self is self.protocol.self_buddy:
  50.             profile = profile
  51.             import common
  52.             profile.account_manager.buddywatcher.unregister(self)
  53.         
  54.         self.CID = 0
  55.         self.space = None
  56.         if self._space:
  57.             
  58.             try:
  59.                 self.update_contact_card(tag(self._space))
  60.             self._space = None
  61.  
  62.         
  63.         self.mships = { }
  64.         self.contactsoap = None
  65.         self.membersoap = None
  66.         self._btype = 'im'
  67.  
  68.     
  69.     def __hash__(self):
  70.         return common.buddy.__hash__(self)
  71.  
  72.     
  73.     def sms(self):
  74.         if validate_sms(self.phone_mobile):
  75.             return self.phone_mobile
  76.         else:
  77.             return False
  78.  
  79.     sms = property(sms)
  80.     
  81.     def _get_phone_mobile(self):
  82.         return getattr(self, '_phone_mobile', None)
  83.  
  84.     
  85.     def _set_phone_mobile(self, val):
  86.         if val and val.startswith('tel:'):
  87.             val = val[4:]
  88.         
  89.         self._phone_mobile = val
  90.  
  91.     phone_mobile = property(_get_phone_mobile, _set_phone_mobile)
  92.     
  93.     def id(self):
  94.         if self.contactsoap is not None:
  95.             v = self.contactsoap.ContactId
  96.             if v:
  97.                 return v
  98.             
  99.         
  100.         if not self.guid:
  101.             pass
  102.         return self.name
  103.  
  104.     id = property(id)
  105.     
  106.     def _get_guid(self):
  107.         return getattr(self, '_guid', None)
  108.  
  109.     
  110.     def _set_guid(self, val):
  111.         if not isinstance(val, self.protocol.ns.cid_class):
  112.             raise TypeError('Was expecting type %r for guid, got %r (type=%r)', self.protocol.cid_class, val, type(val))
  113.         
  114.         self._guid = val
  115.  
  116.     guid = property(_get_guid, _set_guid, doc = 'Contact ID, which takes the form of a UUID in modern versions of MSNP.In the past, it was simply the passport name of the contact.')
  117.     
  118.     def get_caps(self):
  119.         caps = caps
  120.         import common
  121.         if self.status == 'mobile':
  122.             return set([
  123.                 caps.SMS,
  124.                 caps.EMAIL])
  125.         else:
  126.             buddy_caps = set(self.protocol.caps)
  127.             if not self.online:
  128.                 buddy_caps.discard(caps.FILES)
  129.             
  130.             return buddy_caps
  131.  
  132.     caps = property(get_caps)
  133.     
  134.     def _set_contactsoap(self, val):
  135.         self._contactsoap = val
  136.         if self.contactsoap is not None:
  137.             phs = self.contactsoap.ContactInfo.Phones
  138.             if phs is not None and phs.ContactPhone is not None:
  139.                 for phone in phs.ContactPhone:
  140.                     if phone.ContactPhoneType == 'ContactPhoneMobile':
  141.                         
  142.                         try:
  143.                             num = normalize_sms(phone.Number)
  144.                         except:
  145.                             continue
  146.  
  147.                         if not validate_sms(num):
  148.                             continue
  149.                         
  150.                         self.phone_mobile = num
  151.                         continue
  152.                 
  153.             
  154.         
  155.  
  156.     
  157.     def _get_contactsoap(self):
  158.         return self._contactsoap
  159.  
  160.     contactsoap = property(_get_contactsoap, _set_contactsoap)
  161.     
  162.     def online(self):
  163.         return self.status not in ('offline', 'unknown')
  164.  
  165.     online = property(online)
  166.     
  167.     def set_status(self, newval):
  168.         oldstatus = self._status
  169.         self._status = newval
  170.         if oldstatus != newval and self._got_presence:
  171.             self.notify('status', oldstatus, newval)
  172.         
  173.  
  174.     
  175.     def get_status(self):
  176.         if not self._got_presence:
  177.             return 'unknown'
  178.         
  179.         if self._status == 'idle':
  180.             return 'idle'
  181.         
  182.         if self.away:
  183.             return 'away'
  184.         
  185.         if self.mobile:
  186.             return 'mobile'
  187.         
  188.         return self._status
  189.  
  190.     status = ObservableProperty(get_status, set_status, observe = ('_status',))
  191.     
  192.     def profile(self):
  193.         pass
  194.  
  195.     profile = property(profile)
  196.     
  197.     def away(self):
  198.         return self._status in ('busy', 'brb', 'away', 'phone', 'lunch')
  199.  
  200.     away = property(away)
  201.     
  202.     def blocked(self):
  203.         return self in self.protocol.block_list
  204.  
  205.     blocked = property(blocked)
  206.     
  207.     def stripped_msg(self):
  208.         return self.status_message
  209.  
  210.     stripped_msg = property(stripped_msg)
  211.     
  212.     def _set_status_message(self, val):
  213.         self._got_presence = True
  214.         self._status_message = val
  215.  
  216.     
  217.     def _get_status_message(self):
  218.         return self._status_message
  219.  
  220.     status_message = property(_get_status_message, _set_status_message)
  221.     
  222.     def mobile(self):
  223.         if self.sms or self.allow_mobile == 'Y':
  224.             pass
  225.         return self._status not in ('available', 'idle', 'available')
  226.  
  227.     mobile = property(mobile)
  228.     
  229.     def get_profile(self):
  230.         self.protocol.get_profile(self)
  231.  
  232.     get_profile = common.action((lambda self: True))(get_profile)
  233.     
  234.     def sightly_status(self):
  235.         if self.status == 'mobile':
  236.             return _('Mobile')
  237.         else:
  238.             return statuses.get(self._status, self._status.title())
  239.  
  240.     sightly_status = property(sightly_status)
  241.     
  242.     def block(self, _block = True, callback = None):
  243.         self.protocol.block_buddy(self, _block, callback = callback)
  244.  
  245.     block = callsback(block)
  246.     
  247.     def unblock(self, callback = None):
  248.         self.protocol.block_buddy(self, False, callback = callback)
  249.  
  250.     unblock = callsback(unblock)
  251.     
  252.     def service(self):
  253.         num_or_str = get(self, '_btype', 1)
  254.         num = get(dict(msn = 1, mob = 4, fed = 32), num_or_str, num_or_str)
  255.         if num == 32 and self.name.endswith('yahoo.com'):
  256.             prot_name = 'yahoo'
  257.         else:
  258.             prot_name = self.protocol.name
  259.         return prot_name
  260.  
  261.     service = property(service)
  262.     
  263.     def __repr__(self):
  264.         return '<MSNBuddy %s>' % self.name
  265.  
  266.     
  267.     def __str__(self):
  268.         return repr(self)
  269.  
  270.     
  271.     def get_idle(self):
  272.         return self.status == 'idle'
  273.  
  274.     
  275.     def set_idle(self, val):
  276.         pass
  277.  
  278.     idle = ObservableProperty(get_idle, set_idle, observe = ('status',))
  279.     
  280.     def update(self, new):
  281.         if not new:
  282.             return None
  283.         
  284.         for field in new.__dict__:
  285.             newval = getattr(new, field)
  286.             if newval:
  287.                 setattr(self, field, newval)
  288.                 self.setnotifyif()
  289.                 continue
  290.         
  291.  
  292.     
  293.     def update_contact_card(self, card):
  294.         if not card:
  295.             return None
  296.         
  297.         self._space = card._to_xml(pretty = False)
  298.         self.space = MSNSpace(self, card)
  299.  
  300.     
  301.     def _update_ccard_elt(self, elt, kind):
  302.         return dyn_dispatch(self, '_update_ccard_%s' % kind.lower(), elt)
  303.  
  304.     
  305.     def _update_ccard_spacetitle(self, elt):
  306.         self.space.update((lambda .0: for e in .0:
  307. if 'type' not in e._attrs:
  308. (e._name, e._cdata)continue)(elt))
  309.  
  310.     
  311.     def _update_ccard_album(self, elt):
  312.         photos = []
  313.         for subel in elt:
  314.             if subel._attrs.get('type', '') == 'Photo':
  315.                 photos.append(Storage((lambda .0: for e in .0:
  316. (e._name, e._cdata))(subel)))
  317.                 continue
  318.         
  319.         album = self.space.setdefault('album', Storage())
  320.         album.update((lambda .0: for e in .0:
  321. if 'type' not in e._attrs:
  322. (e._name, e._cdata)continue)(elt))
  323.         album.photos = photos
  324.  
  325.     _space = cproperty('')
  326.     
  327.     def _update_ccard_musiclist(self, elt):
  328.         musiclist = self.space.setdefault('musiclist', Storage())
  329.         songs = []
  330.         for song in elt:
  331.             if song._attrs.get('type', '') == 'MusicListEntry':
  332.                 songs.append(Storage((lambda .0: for e in .0:
  333. (e._name, e._cdata))(song)))
  334.                 continue
  335.         
  336.         musiclist.update((lambda .0: for e in .0:
  337. if 'type' not in e._attrs:
  338. (e._name, e._cdata)continue)(elt))
  339.         musiclist.songs = songs
  340.  
  341.     
  342.     def _update_ccard_booklist(self, elt):
  343.         booklist = self.space.setdefault('booklist', Storage())
  344.         books = []
  345.         for book in elt:
  346.             if book._attrs.get('type', '') == 'BookListEntry':
  347.                 books.append(Storage((lambda .0: for e in .0:
  348. (e._name, e._cdata))(book)))
  349.                 continue
  350.         
  351.         booklist.update((lambda .0: for e in .0:
  352. if 'type' not in e._attrs:
  353. (e._name, e._cdata)continue)(elt))
  354.         booklist.books = books
  355.  
  356.     
  357.     def _update_ccard_genericlist(self, elt):
  358.         gen_lists = self.space.setdefault('gen_lists', [])
  359.         entries = []
  360.         for entry in elt:
  361.             if entry._attrs.get('type', '') == 'GenericListEntry':
  362.                 entries.append(Storage((lambda .0: for e in .0:
  363. (e._name, e._cdata))(entry)))
  364.                 continue
  365.         
  366.         new_list = Storage((lambda .0: for e in .0:
  367. if 'type' not in e._attrs:
  368. (e._name, e._cdata)continue)(elt))
  369.         new_list.entries = entries
  370.         gen_lists.append(new_list)
  371.  
  372.     
  373.     def _update_ccard_blog(self, elt):
  374.         blog = self.space.setdefault('blog', Storage())
  375.         posts = []
  376.         for post in elt:
  377.             if post._attrs.get('type', '') == 'Post':
  378.                 posts.append(Storage((lambda .0: for e in .0:
  379. (e._name, e._cdata))(post)))
  380.                 continue
  381.         
  382.         blog.update((lambda .0: for e in .0:
  383. if 'type' not in e._attrs:
  384. (e._name, e._cdata)continue)(elt))
  385.         blog.posts = posts
  386.  
  387.     
  388.     def _update_ccard_profile(self, elt):
  389.         profiles = self.space.setdefault('profiles', Storage())
  390.         for profile in elt:
  391.             p_type = profile._attrs.get('type', '')
  392.             if p_type.endswith('Profile'):
  393.                 prof = profiles.setdefault(p_type.lower()[:-7], Storage())
  394.                 prof.update((lambda .0: for e in .0:
  395. (e._name, e._cdata))(profile))
  396.                 continue
  397.         
  398.         profiles.update((lambda .0: for e in .0:
  399. if 'type' not in e._attrs:
  400. (e._name, e._cdata)continue)(elt))
  401.  
  402.     
  403.     def _update_ccard_livecontact(self, elt):
  404.         pass
  405.  
  406.     
  407.     def __cmp__(self, other):
  408.         
  409.         try:
  410.             if other is self:
  411.                 return 0
  412.             
  413.             return cmp((self.name, self.protocol), (other.name, other.protocol))
  414.         except:
  415.             return -1
  416.  
  417.  
  418.     
  419.     def pretty_profile(self):
  420.         d = { }
  421.         if self.remote_alias and self.alias != self.remote_alias:
  422.             d[_('Display Name:')] = self.remote_alias
  423.         
  424.  
  425.     pretty_profile = property(pretty_profile)
  426.  
  427.  
  428. class MSNSpaceElement(object):
  429.     
  430.     def __init__(self, elt):
  431.         object.__init__(self)
  432.         for attr in ('title', 'url', 'description', 'tooltip'):
  433.             setattr(self, attr, str(getattr(elt, attr, '')).decode('utf-8'))
  434.         
  435.         self.last_check = yesterday()
  436.         self.last_update = yesterday()
  437.  
  438.     
  439.     def __repr__(self):
  440.         res = [
  441.             '<%s ' % type(self).__name__]
  442.         for attr in ('title', 'url', 'description', 'tooltip'):
  443.             myval = getattr(self, attr)
  444.             if myval:
  445.                 res.append('%s=%s, ' % (attr.capitalize(), myval))
  446.                 continue
  447.         
  448.         res[-1] = res[-1][:-2] + '>'
  449.         return ''.join(res)
  450.  
  451.     
  452.     def __iter__(self):
  453.         
  454.         def attrs():
  455.             for attr in ('title', 'url', 'description', 'tooltip'):
  456.                 val = getattr(self, attr)
  457.                 if val:
  458.                     yield (attr, val)
  459.                     continue
  460.             
  461.  
  462.         import itertools as itertools
  463.         return itertools.chain(attrs(), iter(self.contents))
  464.  
  465.     
  466.     def pretty_profile(self, p = None):
  467.         if not p:
  468.             pass
  469.         p = odict()
  470.         p[self.title + ':'] = [
  471.             '\n',
  472.             (self.url, self.description)]
  473.         return p
  474.  
  475.     pretty_profile = property(pretty_profile)
  476.     
  477.     def to_tag(self):
  478.         table = tag('table')
  479.         tr = tag('tr')
  480.         a = tag('a', href = self.url)
  481.         a._add_child(tag('b', self.title + ':'))
  482.         tr._add_child(a)
  483.         tr._add_child(tag('td', self.description))
  484.         table._add_child(tr)
  485.         return table
  486.  
  487.  
  488.  
  489. class SpaceTitleElt(MSNSpaceElement):
  490.     
  491.     def to_tag(self):
  492.         a = tag('a', href = self.url)
  493.         a._add_child('b', self.title)
  494.         return a
  495.  
  496.     
  497.     def pretty_profile(self):
  498.         return { }
  499.  
  500.     pretty_profile = property(pretty_profile)
  501.  
  502.  
  503. class GenericListElt(list, MSNSpaceElement):
  504.     
  505.     def __init__(self, elt):
  506.         MSNSpaceElement.__init__(self, elt)
  507.         []([], _[1])
  508.  
  509.     
  510.     def __repr__(self):
  511.         return '<%s: %s>' % (type(self).__name__, list.__repr__(self))
  512.  
  513.     
  514.     def __iter__(self):
  515.         return list.__iter__(self)
  516.  
  517.     
  518.     def pretty_profile(self):
  519.         return odict(MSNSpaceElement.pretty_profile.fget(self).items())
  520.  
  521.     pretty_profile = property(pretty_profile)
  522.     
  523.     def to_tag(self):
  524.         p = MSNSpaceElement.to_tag(self)
  525.         for thing in self:
  526.             p._add_child(thing.to_tag())
  527.         
  528.         return p
  529.  
  530.  
  531.  
  532. class MSNSpace(MSNSpaceElement):
  533.     _name = 'MSN Space'
  534.     
  535.     def __init__(self, buddy, contact_card):
  536.         MSNSpaceElement.__init__(self, contact_card)
  537.         self.last_update = str(contact_card.lastUpdate)
  538.         self.title = contact_card._attrs.get('displayName', buddy.name).strip().decode('utf-8')
  539.         self.buddy = buddy
  540.         self.dp_url = contact_card._attrs.get('displayPictureUrl', '')
  541.         self.contents = []
  542.         for element in contact_card.elements:
  543.             type_ = element['type']
  544.             cls = getattr(thismod, '%sElt' % type_, MSNSpaceElement)
  545.             self.contents.append(cls(element))
  546.         
  547.  
  548.     
  549.     def pretty_profile(self):
  550.         p = odict()
  551.         for thing in self.contents:
  552.             p.update(thing.pretty_profile.items())
  553.         
  554.         url = self.contents[0].url
  555.         if not url and self.buddy.CID:
  556.             url = 'http://spaces.live.com/Profile.aspx?cid=%s' % self.buddy.CID
  557.         elif not self.buddy.CID:
  558.             return p
  559.         
  560.         p['Profile URL:'] = [
  561.             '\n',
  562.             (url, url)]
  563.         return p
  564.  
  565.     pretty_profile = property(pretty_profile)
  566.     
  567.     def to_tag(self):
  568.         p = tag('p')
  569.         for thing in self.contents:
  570.             p._add_child(thing.to_tag())
  571.         
  572.         return p
  573.  
  574.  
  575.  
  576. class GenericListEntryElt(MSNSpaceElement):
  577.     
  578.     def __init__(self, elt):
  579.         MSNSpaceElement.__init__(self, elt)
  580.         self.last_update = elt['lastUpdated']
  581.         self.title = str(elt.title).strip().decode('utf-8')
  582.  
  583.     
  584.     def pretty_profile(self):
  585.         return odict({
  586.             self.title: '\n' })
  587.  
  588.     pretty_profile = property(pretty_profile)
  589.  
  590.  
  591. class MusicListElt(GenericListElt):
  592.     
  593.     def pretty_profile(self):
  594.         songlist = [ x.pretty_profile for x in self ]
  595.         songlist.insert(0, '\n')
  596.         return odict({
  597.             'Songs:': songlist })
  598.  
  599.     pretty_profile = property(pretty_profile)
  600.  
  601.  
  602. class MusicListEntryElt(GenericListEntryElt):
  603.     
  604.     def __init__(self, elt):
  605.         GenericListEntryElt.__init__(self, elt)
  606.         self.artist = str(elt.artist).decode('utf-8')
  607.         self.song = str(elt.song).decode('utf-8')
  608.  
  609.     
  610.     def pretty_profile(self):
  611.         return (self.url, _(u'%s by %s') % (self.song, self.artist + u'\n'))
  612.  
  613.     pretty_profile = property(pretty_profile)
  614.  
  615.  
  616. class BookListElt(GenericListElt):
  617.     
  618.     def to_tag(self):
  619.         p = tag('p')
  620.         p._add_child(tag('a', self.title, href = self.url))
  621.         for thing in self:
  622.             p._add_child(thing.to_tag())
  623.         
  624.         return p
  625.  
  626.  
  627.  
  628. class BookListEntryElt(GenericListEntryElt):
  629.     
  630.     def to_tag(self):
  631.         return tag('p', '%s<br />%s' % (self.title, self.description))
  632.  
  633.  
  634.  
  635. class BlogElt(GenericListElt):
  636.     
  637.     def to_tag(self):
  638.         p = tag('p')
  639.         for thing in self:
  640.             p._add_child(thing.to_tag())
  641.         
  642.         return p
  643.  
  644.     
  645.     def pretty_profile(self):
  646.         return []([ post.pretty_profile for post in self ])
  647.  
  648.     pretty_profile = property(pretty_profile)
  649.  
  650.  
  651. class PostElt(GenericListEntryElt):
  652.     
  653.     def to_tag(self):
  654.         link = tag('a', self.title, href = self.url)
  655.         return tag('p', 'New post: %s<br />%s' % (link._to_xml(), self.description))
  656.  
  657.     
  658.     def pretty_profile(self):
  659.         return (self.title + ':', [
  660.             '\n',
  661.             (self.url, self.description)])
  662.  
  663.     pretty_profile = property(pretty_profile)
  664.  
  665.  
  666. class AlbumElt(GenericListElt):
  667.     
  668.     def to_tag(self):
  669.         p = tag('p')
  670.         tr = tag('b', 'Photos Album:')
  671.         for photo in self:
  672.             tr._add_child(photo.to_tag())
  673.         
  674.         p._add_child(tr)
  675.         return p
  676.  
  677.     
  678.     def pretty_profile(self):
  679.         piclist = [ x.pretty_profile for x in self ]
  680.         piclist.insert(0, '\n')
  681.         return odict({
  682.             'Photos:': piclist })
  683.  
  684.     pretty_profile = property(pretty_profile)
  685.  
  686.  
  687. class PhotoElt(GenericListEntryElt):
  688.     (WIDTH, HEIGHT) = (20, 20)
  689.     
  690.     def __init__(self, elt):
  691.         GenericListEntryElt.__init__(self, elt)
  692.         self.thumbnail_url = str(elt.thumbnailUrl)
  693.         self.web_url = str(elt.webReadyUrl)
  694.         self.album_name = str(elt.albumName).decode('utf-8')
  695.         self.img_sm = None
  696.         self.img_lg = None
  697.         self.loadimages()
  698.  
  699.     
  700.     def to_tag(self):
  701.         a = tag('a', href = self.url)
  702.         a._add_child(tag('img', src = self.web_url, alt = self.tooltip, width = self.WIDTH, height = self.HEIGHT))
  703.         return a
  704.  
  705.     
  706.     def pretty_profile(self):
  707.         pref = pref
  708.         import common
  709.         sz = pref('msn.spaces.photosize', 20)
  710.         if self.img_sm is None:
  711.             return (self.url, u'%s - %s' % (self.album_name, self.title + u'\n'))
  712.         
  713.         return dict(data = self.img_sm, alt = self.tooltip, height = sz, width = sz, href = self.url)
  714.  
  715.     pretty_profile = property(pretty_profile)
  716.     
  717.     def loadimages(self):
  718.         
  719.         try:
  720.             self.img_sm = urlcacheopen(self.thumbnail_url)
  721.         except:
  722.             self.imb_sm = 'javascript'
  723.  
  724.         if self.img_sm and 'javascript' in self.img_sm:
  725.             self.img_sm = None
  726.         
  727.         
  728.         try:
  729.             self.img_lg = urlcacheopen(self.web_url)
  730.         except:
  731.             self.img_lg = 'javascript'
  732.  
  733.         if self.img_lg and 'javascript' in self.img_lg:
  734.             self.img_lg = None
  735.         
  736.  
  737.     loadimages = threaded(loadimages)
  738.  
  739.  
  740. class MSNContact(contacts.Contact):
  741.     _renderer = 'Contact'
  742.     inherited_actions = [
  743.         MSNBuddy]
  744.     
  745.     def __init__(self, buddy, group_obj_or_id):
  746.         group_id = getattr(group_obj_or_id, 'name', group_obj_or_id)
  747.         contacts.Contact.__init__(self, buddy, (buddy.name, group_id))
  748.  
  749.     
  750.     def __repr__(self):
  751.         return '<MSN' + contacts.Contact.__repr__(self)[4:]
  752.  
  753.     
  754.     def block(self, *a, **k):
  755.         return contacts.Contact.block(self, *a, **k)
  756.  
  757.     block = common.action(contacts.Contact._block_pred)(block)
  758.     
  759.     def unblock(self, *a, **k):
  760.         return contacts.Contact.unblock(self, *a, **k)
  761.  
  762.     unblock = common.action(contacts.Contact._unblock_pred)(unblock)
  763.  
  764.  
  765. def from_mime(mime_info, email, msn, friendlyname = None):
  766.     b = msn.get_buddy(email)
  767.     if friendlyname:
  768.         if not url_decode(friendlyname).decode('utf-8'):
  769.             pass
  770.         b.remote_alias = None
  771.     
  772.     info = to_storage(mime_info)
  773.     for k, v in info.items():
  774.         k = k.replace(' ', '_').replace('-', '_').lower()
  775.         setattr(b, k, v)
  776.     
  777.     b.info = dict(info.items())
  778.     return b
  779.  
  780.  
  781. def from_lst(msn, N, **kwargs):
  782.     email = N
  783.     kwargs = to_storage(kwargs)
  784.     b = msn.get_buddy(email)
  785.     if 'F' in kwargs:
  786.         if not url_decode(kwargs['F']).decode('utf-8'):
  787.             pass
  788.         b.remote_alias = None
  789.     
  790.     if 'C' in kwargs:
  791.         b.guid = msn.cid_class(kwargs['C'])
  792.     
  793.     return b
  794.  
  795. CONTACT_CARD = '\n\n<contactCard>\n  <storageAuthCache>\n    1pqFlR36RzjW-X1jmTbKjKRsUpCe4cq9KHls4whqQIXlnjXVMidNbandYSmy0QEqm17M7xLIb2Fvo\n  </storageAuthCache>\n  <elements returnedMatches="3" displayName="shaps" totalMatches="3" displayPictureUrl="http://shared.live.com/jdIfE-NNCwZHMseiLFdi12c3U3v1VRQA5Wr8zoyj4Q0IBYQYt0pq5GcGiVCgriAz-mQjoThX0wVn7RxL!igd-A/base/3379/Controls/img/ContactControl/WLXLarge_default.gif">\n    <element type="SpaceTitle">\n      <title>\n        Steve\'s Space\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/?owner=1\n      </url>\n      <totalNewItems>\n        0\n      </totalNewItems>\n    </element>\n    <element type="BookList">\n      <subElement type="BookListEntry" lastUpdated="0001-01-01T00:00:00">\n        <description>\n          stuff for description\n        </description>\n        <title>\n          book title 3\n        </title>\n        <tooltip>\n          Title: book title 3\n          Author: author\n          Description: stuff for description\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Lists/cns!2DB0770EAE61F13!106?owner=1\n        </url>\n      </subElement>\n      <subElement type="BookListEntry" lastUpdated="0001-01-01T00:00:00">\n        <description>\n          lkj lkj l\n        </description>\n        <title>\n          book title 2\n        </title>\n        <tooltip>\n          Title: book title 2Author: author 2Description: lkj lkj l\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Lists/cns!2DB0770EAE61F13!106?owner=1\n        </url>\n      </subElement>\n      <title>\n        Book List\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/Lists/cns!2DB0770EAE61F13!106?owner=1\n      </url>\n      <description>\n        book title 3\n      </description>\n      <totalNewItems>\n        0\n      </totalNewItems>\n    </element>\n    <element type="Album">\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:32.31-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          10.png\n        </title>\n        <tooltip>\n          January 1710.png\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!151?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxszOikmaP3r3dXbFG7ToyO0hMKW-WHHHJ2D9Lmff30X2Jo-2STreLVcNjaEogTUoBTZHMxhbwHv0ZBeHMwOnEdJcPytZWhE0nfg\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxszOikmaP3r3dlq-_0irNxa_2G2Gzp9ZgHOfSTUGN79t1IYnjSY5DKTXfP4ADxmBKN0Z5m0I7P6TUjGRafqU4NPz05iaCqzwJRA\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:32.107-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          9.gif\n        </title>\n        <tooltip>\n          January 179.gif\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!150?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxswE_zjzklXeXVqyaBoOfjid5rjfn8g0bSHcgQmQ5AoqTuNtwMzT-XNFSFv_IKj8mcZZ-ENJcOYB3XWJYb3U0wC4RSwlFUqeGfw\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxswE_zjzklXeXVJfE1Gdeox-zkKou9QwXud_dSI32qXsAnPABY4K57fs1bpWbe7JSZdhHJwEQ0psi7lGPBDLpdW0_3sUHaRDjuA\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:31.967-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          8.gif\n        </title>\n        <tooltip>\n          January 178.gif\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!149?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs1s-Updfv0-X7Rk9c9exySfJ-2PozaK6BKKyP9v8DAcv5xxyZSZ0OK9wirfRd2yWEEX7VzZS2mvvBkUWbtz0VXbLead2Ybs5OA\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs1s-Updfv0-XLAU_ZUH4P33y-NaJII-4uupQ0uoxjOCQJwxrL6sA1Xa9X3mdKUNYrj95_CVyrr5QVP7BXWFFyd2avflir6OhOA\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:08.043-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          7.png\n        </title>\n        <tooltip>\n          January 177.png\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!148?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxsyrdesm1gzDOdiW3uJUFNjo3H7f9H4uLtkj7u-akJEORhy2lLsKpM01wmbQvBUA-OEZdfQLdJi-NouMArPt5N0CW4nYHxW51UA\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxsyrdesm1gzDO9TYtPpGT2JsdnYIUZw7Lo5-yTazqwfIGHa6vu-Ku9OIMA0gLptXjy6IMXfr-CV5cag2FJKcqo_rVuPRT2t_t7w\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:07.933-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          6.jpg\n        </title>\n        <tooltip>\n          January 176.jpg\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!147?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs-wyD9TggUJ_4XM7tDqJ6CINNBLXvtT4Lfwr7ikxskuBzMQNerpK-oV8CvyWk9BbashoZg1H9afsvyl576NJp4I0FWfrI4hwBw\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs-wyD9TggUJ_C3BxBY1m6WsjbKXjpxDULsHHHEg8yAl2WLKDzSqb99OBxXaSetCdU-p8o6ScLb807p2QFAWr4gtH6OvBof2EBg\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:07.81-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          5.jpg\n        </title>\n        <tooltip>\n          January 175.jpg\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!146?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs49JgAmSpKyqet5yR2Upi4Nn_X5ewL5gr6Tst7QFxHUmmjxQJpMpldLBUCWxs5dXLddQfHtjnSJTjycfX0vvZYvb9vVzlON9OA\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs49JgAmSpKyq-H9kk6kls9jjKFGLEr9AWb3NdQAxCcI6ta_72H6Ct25Dzjm1lkz7_xgUUuKboUy37TR0hveNy5ZRjhJ1eMUzcw\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <title>\n        Photos:\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/Photos/?owner=1\n      </url>\n      <totalNewItems>\n        0\n      </totalNewItems>\n    </element>\n    <element type="Blog">\n      <subElement type="Post" lastUpdated="2007-01-16T13:55:01.013-08:00">\n        <description>\n          sfljks dflkjsdflkjsdflkjs dflkjsad flks jdaflsa dkjflsadkfjsadlfkj\n        </description>\n        <title>\n          NEWNEWNEW\n        </title>\n        <tooltip>\n          sfljks dflkjsdflkjsdflkjs dflkjsad flks jdaflsa dkjflsadkfjsadlfkj\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Blog/cns!2DB0770EAE61F13!130.entry?owner=1\n        </url>\n      </subElement>\n      <title>\n        Blog:\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/?owner=1\n      </url>\n      <totalNewItems>\n        0\n      </totalNewItems>\n    </element>\n    <element type="Profile">\n      <subElement type="GeneralProfile" lastUpdated="2007-01-16T09:01:51.533-08:00">\n        <description />\n        <title>\n          General profile info updated\n        </title>\n        <tooltip>\n          This person has recently added or updated General profile information.\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view\n        </url>\n      </subElement>\n      <subElement type="PublicProfile" lastUpdated="2006-12-21T16:06:47.47-08:00">\n        <description />\n        <title>\n          Public profile info updated\n        </title>\n        <tooltip>\n          This person has recently added or updated Public profile information.\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view\n        </url>\n      </subElement>\n      <subElement type="SocialProfile" lastUpdated="2006-05-31T09:54:52.59-07:00">\n        <description />\n        <title>\n          Social profile info updated\n        </title>\n        <tooltip>\n          This person has recently added or updated Social profile information.\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view\n        </url>\n      </subElement>\n      <title>\n        Profile\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view\n      </url>\n      <description>\n        General profile info updated\n      </description>\n      <totalNewItems>\n        3\n      </totalNewItems>\n    </element>\n    <element type="LiveContact">\n      <subElement type="ProfessionalContactProfile" lastUpdated="2006-12-21T16:06:47.47-08:00">\n        <description />\n        <title>\n          Business contact info updated\n        </title>\n        <tooltip>\n          This person has recently added or updated Business contact information.\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view&mode=activecontacts\n        </url>\n      </subElement>\n      <subElement type="PersonalContactProfile" lastUpdated="2006-12-21T16:06:47.47-08:00">\n        <description />\n        <title>\n          Personal contact info updated\n        </title>\n        <tooltip>\n          This person has recently added or updated Personal contact information.\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view&mode=activecontacts\n        </url>\n      </subElement>\n      <title>\n        Contact Info\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view&mode=activecontacts\n      </url>\n      <description>\n        Business contact info updated\n      </description>\n      <totalNewItems>\n        2\n      </totalNewItems>\n    </element>\n  </elements>\n  <lastUpdate>\n    2007-04-18T08:20:01.167-07:00\n  </lastUpdate>\n  <theme>\n    <name>\n      personalspacegree\n    </name>\n    <titleBar foreground="333333" fontFace="" background="f4fbf7" />\n    <clientArea foreground="444444" backgroundImage="http://shared.live.com/jdIfE-NNCwb0XZTwVBd6PpCWk!2k7FEfmc0OX5OC0rZ-I0WjzyccY5aYuUiTkMo2blaFQRxUooU/personalspacegree/3379/img/green_card_bkgd.gif" fontFace="" background="FFFFFF" />\n    <toolbar foreground="333333" fontFace="" background="f4fbf7" />\n    <border topLeftImage="http://sc1.sclive.net/11.01.3810.0000/Web/Contacts/images/card_ul.gif" bottomLeftImage="http://sc2.sclive.net/11.01.3810.0000/Web/Contacts/images/card_ll.gif" bottomRightImage="http://sc4.sclive.net/11.01.3810.0000/Web/Contacts/images/card_lr.gif" outline="7F7F7F" topRightImage="http://sc3.sclive.net/11.01.3810.0000/Web/Contacts/images/card_ur.gif" />\n  </theme>\n  <liveTheme>\n    <themeName>\n      personalspacegree\n    </themeName>\n    <head backgroundImage="http://shared.live.com/jdIfE-NNCwb0XZTwVBd6PpCWk!2k7FEfmc0OX5OC0rZ-I0WjzyccY5aYuUiTkMo2blaFQRxUooU/personalspacegree/3379/img/SmallBannerImage.jpg" textColor="333333" linkColor="006629" backgroundColor="f4fbf7" />\n    <body accordionHoverColor="aad2ba" secondaryLinkColor="7F7F7F" dividerColor="8ed4ab" backgroundImage="" backgroundColor="f4fbf7" linkColor="006629" textColor="333333" />\n    <actions linkColor="333333" backgroundColor="f4fbf7" />\n  </liveTheme>\n</contactCard>\n'.strip().replace('&', '&')
  796. if __name__ == '__main__':
  797.     b = Storage(name = 'shaps776@hotmail.com')
  798.     card = MSNSpace(b, tag(CONTACT_CARD))
  799.     print card.to_tag()._to_xml()
  800.  
  801.